
//# Pipeline Half-Buffer

// A single pipeline register with ready/valid handshakes.  Decouples the
// input and ouput handshakes (no combinational path), but does not allow
// concurrent read/write like a full [Pipeline Skid Buffer](./Pipeline_Skid_Buffer.html).
// The Half-Buffer must be read out before you
// can write into it again, halving the maximum bandwidth (except in Circular
// Buffer Mode).

// However, using a Half-Buffer can improve the throughput of a long-running
// module with ready/valid handshakes, where the module input will not accept
// new data until the module output is read out, and it takes multiple cycles
// to compute a result. With a Half-Buffer, the module can immediately dump
// its output into the Half-Buffer and then accept new input data, overlapping
// another computation with the wait time until the final destination reads
// out the Half-Buffer.

// A Half-Buffer can also implement a very useful control mechanism by
// signalling to the source when the next item can be processed.  After
// accepting an item at the input, detecting the rise of valid at the output
// with a [Pulse Generator](./Pulse_Generator.html) starts the internal logic,
// whose control logic now only need to pulse ready when the calculation is
// done to complete the handshake. This simplifies control and maintains
// concurrency.


//## Circular Buffer Mode

// Normally, a Half-Buffer reads in one value and will not complete another
// input handshake until the data has been read out. You can think of this as
// buffering the *earliest* value from the pipeline.

// Setting `CIRCULAR_BUFFER` parameter to a non-zero value changes the
// behaviour at the input: the input handshake can always complete, discarding
// the data already in the buffer even if it was never read out.  You can
// think of this as buffering the *latest* value from the pipeline.  This is
// a one-entry circular buffer.

// However, data in the buffer can only be read out *once*, as usual, until
// updated again at the input, possibly in the same clock cycle. Simultaneous
// input and output handshakes are possible in Circular Buffer Mode since
// `input_ready` no longer depends on the empty/full state of the buffer
// (which forces alternation of input and output handshakes), nor on the state
// of the output handshake (which is disallowed to prevent creating
// a combinational path between input and output).

`default_nettype none

module Pipeline_Half_Buffer
#(
    parameter WORD_WIDTH            = 0,
    parameter CIRCULAR_BUFFER       = 0     // non-zero to enable
)
(
    input  wire                     clock,
    input  wire                     clear,

    input  wire                     input_valid,
    output reg                      input_ready,
    input  wire [WORD_WIDTH-1:0]    input_data,

    output reg                      output_valid,
    input  wire                     output_ready,
    output wire [WORD_WIDTH-1:0]    output_data
);

    localparam WORD_ZERO = {WORD_WIDTH{1'b0}};

// Storage for the data

    reg half_buffer_load = 1'b0;

    Register
    #(
        .WORD_WIDTH     (WORD_WIDTH),
        .RESET_VALUE    (WORD_ZERO)
    )
    half_buffer
    (
        .clock          (clock),
        .clock_enable   (half_buffer_load),
        .clear          (clear),
        .data_in        (input_data),
        .data_out       (output_data)
    );

// And an empty/full bit associated with the data storage.

    reg  set_to_empty = 1'b0;
    reg  set_to_full  = 1'b0;
    wire buffer_full;

    Register
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b0)
    )
    empty_full
    (
        .clock          (clock),
        .clock_enable   (set_to_full),
        .clear          (set_to_empty),
        .data_in        (1'b1),
        .data_out       (buffer_full)
    );

// Then, from the state of the empty/full bit and the signals local only to
// each handshake (no path between them), determine when data can transfer.
// Note that we must be empty before we can `set_to_full`. Anything else
// creates a combinational path from the input handshake to the output
// handshake.

// EXCEPTION: In Circular Buffer Mode, `input_ready` does not depend on any
// other logic, which enables simultaneous input and output handhsakes without
// combinational paths between them.

    always @(*) begin
        input_ready      = (buffer_full   == 1'b0) || (CIRCULAR_BUFFER != 0);
        output_valid     = (buffer_full   == 1'b1);
        set_to_full      = (input_valid   == 1'b1) && (input_ready  == 1'b1);
        set_to_empty     = (output_valid  == 1'b1) && (output_ready == 1'b1) && (set_to_full == 1'b0);
        set_to_empty     = (set_to_empty  == 1'b1) || (clear == 1'b1);
        half_buffer_load = (set_to_full   == 1'b1);
    end

endmodule

